﻿using System;
using System.Collections.Generic;
using Draco.OWF.Metadata.DataBinding;
using Draco.OWF.Metadata;
using Draco.OWF.RT.Engine.Worker;
using System.Threading;

namespace Draco.OWF.RT.Impl
{
    public abstract class AbstractJob : IJob
    {
        protected String m_Id = Guid.NewGuid().ToString();
        protected String m_Name;
        protected WorkflowModel workflow;
        protected BindingData m_startParameters;
        protected JobStatus m_Status = JobStatus.Inactive;
        protected List<ITask> JobTasks = new List<ITask>();
        protected List<IJobPath> JobPaths = new List<IJobPath>();

        #region constructor
        /// <summary>
        /// constructor
        /// </summary>
        /// <param name="processTemplate"></param>
        /// <param name="startParameters"></param>
        public AbstractJob(WorkflowModel processTemplate)
        {
            if (processTemplate == null)
                throw new ArgumentNullException("processTemplate is null");
            this.workflow = processTemplate;
        }
        #endregion

        #region property
        /// <summary>
        /// get or set Id
        /// </summary>
        public string Id
        {
            get { return m_Id; }
            set
            {
                if (!String.IsNullOrEmpty(value))
                    this.m_Id = value;
            }
        }
        /// <summary>
        /// get or set Name
        /// </summary>
        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }
        /// <summary>
        /// 
        /// </summary>
        public virtual JobStatus Status
        {
            get { return m_Status; }
            set { m_Status = value; }
        }

        public virtual WorkflowModel WorkflowDefine
        {
            get { return workflow; }
        }
        #endregion

        /// <summary>
        /// 
        /// </summary>
        public virtual void Initialize()
        {
        }

        public void AddTask(ITask acIns)
        {
            if (acIns == null)
                throw new ArgumentNullException();
            ITask tar = GetTask(acIns.Id);
            if (tar == null)
                this.JobTasks.Add(acIns);
        }

        public void AddRouteInstance(IJobPath routeIns)
        {
            if (routeIns == null)
                throw new ArgumentNullException();
            IJobPath tar = GetRouteInstance(routeIns.Id);
            if (tar == null)
            {
                this.JobPaths.Add(routeIns);
            }
        }


        public ITask GetTask(string AcInsId)
        {
            foreach (var ac in this.JobTasks)
            {
                if (ac.Id == AcInsId)
                    return ac;
            }
            return null;
        }

        public IJobPath GetRouteInstance(string routeInsId)
        {
            foreach (var r in this.JobPaths)
            {
                if (r.Id == routeInsId)
                    return r;
            }
            return null;
        }

        public void Start(IJobContext jobContext, BindingData arguments)
        {
            IWorkerEngine worker = CreateWorkerEngine(this);
            ThreadPool.QueueUserWorkItem(new WaitCallback(worker.Start), arguments);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="process"></param>
        /// <param name="parentContext"></param>
        /// <returns></returns>
        protected virtual IWorkerEngine CreateWorkerEngine(IJob job)
        {
            return new WorkerEngine(job);
        }


        public IList<ITask> MoveNext(IJobContext jobContext, IList<ITask> laskTaks)
        {
            IList<ITask> currentTasks = new List<ITask>();//current tasks
            if (laskTaks == null || laskTaks.Count==0)
            {
                //first node, ready for start
                ITask cTask = CreateTask(this.workflow.GetEntry(), null);
                currentTasks.Add(cTask);
                this.JobTasks.Add(cTask);
                this.Status = JobStatus.Running;
            }
            else
            {
                //Check if job is finished
                bool complete = true;
                foreach (var task in laskTaks)
                {
                    if (task.Activity.Id != this.workflow.EndActivity.Id)
                    {
                        complete = false;
                        break;
                    }
                }
                if (complete)// finished, return
                {
                    this.Status = JobStatus.Completed;
                    return laskTaks;
                }
                //runing
                foreach (var ltask in laskTaks)
                {
                    if (ltask.Status == TaskStatus.Completed)
                    {
                        IList<ActivityModel> activities = this.GetValidSubsequenceActivities(ltask.Activity, jobContext);
                        if (activities != null && activities.Count > 0) //
                        {
                            foreach (var activity in activities)
                            {
                                ITask cTask = GetSynchronizingTask(activity, jobContext);
                                if (cTask == null)
                                {
                                    //move next
                                    cTask = CreateTask(activity, ltask);
                                    this.JobTasks.Add(cTask);
                                }
                                IJobPath path = new JobPath() { FromTaskId = ltask.Id, ToTaskId = cTask.Id };
                                this.JobPaths.Add(path);
                                currentTasks.Add(cTask);
                            }
                        }
                        else
                        {
                            throw new Exception("Cannot find valid subsequence activity of activity:" + ltask.Activity.Name);
                        }
                    }
                    else if(ltask.Status == TaskStatus.Wait)
                    {
                        //try to run it again
                        currentTasks.Add(ltask);
                    }
                }
            }

            foreach (var task in currentTasks)
            {
                ITaskContext taskCtx = new TaskContext(jobContext);
                task.Run(taskCtx);
            }
            
            return currentTasks;
        }

        protected ITask CreateTask(ActivityModel activity,ITask lastTaskReference)
        {
            ITask task = new Task(activity);
            return task;
        }

        protected IList<ActivityModel> GetValidSubsequenceActivities(ActivityModel activity, IJobContext context)
        {
            IList<ActivityModel> activities = new List<ActivityModel>();
            IList <RouteModel> routes = this.workflow.GetFromActivityRoutes(activity.Id);
            foreach (var route in routes)
            {
                if (CheckRoute(route, context))
                {
                    ActivityModel ac = this.workflow.GetActivity(route.ToId);
                    activities.Add(ac);
                }
            }
            return activities;
        }

        protected bool CheckRoute(RouteModel route,IJobContext context)
        {
            return true;
        }
        protected ITask GetSynchronizingTask(ActivityModel activity, IJobContext context)
        {
            return null;
        }
    }
}
